﻿using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Diagnostics;
using System.Drawing;
using System.IO;
using System.Windows.Forms;
using GMap.NET;
using GMap.NET.MapProviders;
using GMap.NET.WindowsForms;
using GMap.NET.WindowsForms.Markers;
using Itinero;
using NLog;
using ShkSimulation.carsharing;
using ShkSimulation.core.util.osm;
using ShkSimulation.desktop.component.tools;
using ShkSimulation.desktop.setting;
using ShkSimulation.desktop.util;
using ShkSimulation.carsharing.impl;
using ShkSimulation.carsharing.impl.elements;
using ShkSimulation.carsharing.impl.model;
using Vehicle = Itinero.Osm.Vehicles.Vehicle;

namespace ShkSimulation.desktop.component {
    public partial class RealMapControl : UserControl, ResetSimulation {
        
        protected static readonly Logger logger = LogManager.GetLogger("desktop");

        private Dictionary<Station, PointLatLng> Stations;

        private Bound AgencyBound { get; set; } = new Bound(double.MaxValue, double.MaxValue, double.MinValue, double.MinValue);

        public readonly string UrlPattern = "https://www.openstreetmap.org/api/0.6/map?bbox={0},{1},{2},{3}";
            
        public readonly string OverpassPattern = "https://overpass-api.de/api/map?bbox={0},{1},{2},{3}";
        
        private Itinero.Profiles.Vehicle[] Profiles;

        public RealMapControl() {
            InitializeComponent();
            Init();
        }
        
        private void Init() {
            this.Load += MapShow_Begin;
            this.Load += LoadOsmFileButton_Click;
            FileTextBox.Text = Path.GetFullPath(@"../../../data/chengdu.osm");
            DownloadOsmTextBox.Text = Environment.GetFolderPath(Environment.SpecialFolder.Desktop) + @"\overpass.routerdb";
            Profiles = new[] { Vehicle.Car };
        }

        private void LoadBackground(object sender, EventArgs eventArgs) {
            Invoke(new Action(() => {
                LoadOsmFileButton_Click(sender, eventArgs);
                MapShow_Begin(sender, eventArgs);
            }));
        }

        private SimulationApplication application;
        private OpenStreetMapRoutingProvider MapRoutingProvider;
        
        public void SetSimulationApplication(SimulationApplication app) {
            this.application = app;
            application.AfterInitSuccess += AfterInitSuccess;
            application.OnRouteSuccess += ApplicationOnRouteSuccess;
        }
        
        public void Reset() {
            application = null;
            // MapRoutingProvider = null;
            Stations = new Dictionary<Station, PointLatLng>();
            GMapControl.Controls.Clear();
            GMapControl.Overlays.Clear();
            GMapControl.SelectedArea = RectLatLng.Empty;
            foreach (var mapMarker in markersOverlay.Markers) {
                mapMarker.ToolTip = null;
            }
            markersOverlay.Clear();
            GMapControl.Overlays.Add(markersOverlay);
            GMapControl.ZoomAndCenterMarkers(markersOverlay.Id);
            CurrentRoute = null;
            CurrentStart = null;
            CurrentEnd = null;
            StartIndex = true;
        }

        private void ApplicationOnRouteSuccess(Route route, Road road) {
            if (MapRoutingProvider == null) {
                return;
            }
            List<PointLatLng> points = MapRoutingProvider.ResultRouteToPoints(route);
            string name = $"{road.Name}";
            MapRoute result = new MapRoute(points, name);
            result.Tag = route;
            this.Invoke(new Action(() => {
                DrawRoute(result);
            }));
        }

        private void AfterInitSuccess(SimulationInfo obj) {
            Stations = new Dictionary<Station, PointLatLng>();
            foreach (var agency in obj.NetworkInputData.ClassifiedAgencies[CarsharingConfig.StationResourceKey]) {
                if (agency is Station station) {
                    double latitude = station.Latitude;
                    double longitude = station.Longitude;
                    AgencyBound.UpdateBound(latitude, longitude);
                    Stations.Add(station, new PointLatLng(latitude, longitude));
                    AgencyMarker marker = AgencyMarker.Create(station, Properties.Resources.station);
                    markersOverlay.Markers.Add(marker);
                }
            }
        }
        
        private void MapShow_Begin(object sender, EventArgs e) {
            if (MapShowWorker.IsBusy) {
                Console.WriteLine($@"正在加载路网背景，请稍后");
                return;
            }
            MapShowWorker.DoWork += (_, _) => MapShow();
            MapShowWorker.RunWorkerAsync();
        }

        private void MapShow() {
            Console.WriteLine($@"开始加载背景: {DateTime.Now}");
            GMapControl.Invoke(new Action(InternalShowMap));
            Console.WriteLine($@"加载路网背景成功: {DateTime.Now}"); 
            void InternalShowMap() {
                //Bing混合地图
                GMapControl.OnSelectionChange += GMapControlSelectionAreaChange;
                GMapControl.OnMarkerEnter += GMapControlOnMarkerEnter;
                GMapControl.OnMarkerClick += GMapControlOnMarkerClick;
                GMapControl.MapProvider = GMapProviders.ArcGIS_World_Street_Map;
                //离线模式
                GMapControl.Manager.Mode = AccessMode.ServerAndCache;
                String mapPath = Application.StartupPath + "/ArcGIS_World_Street_Map.gmdb";
                GMaps.Instance.ImportFromGMDB(mapPath);
                GMapControl.Position = new PointLatLng(30.66376501,104.0635353);
                GMapControl.ShowCenter = false;
                GMapControl.DragButton = MouseButtons.Left;
                GMapControl.SelectionPen.Color = Color.FromArgb(0, 183, 255);
                GMapControl.MinZoom = 1;
                GMapControl.MaxZoom = 19;
                GMapControl.MouseWheelZoomType = MouseWheelZoomType.MousePositionWithoutCenter;
                GMapControl.Zoom = 13;
                //GMapControl.Overlays.Add(markersOverlay);
                GMapControl.ZoomAndCenterMarkers(markersOverlay.Id);
                void GMapControlOnMarkerClick(GMapMarker item, MouseEventArgs mouseEventArgs) {
                    if ((mouseEventArgs.Button & MouseButtons.Left) != 0 && item is AgencyMarker marker) {
                        double latitude = marker.Latitude;
                        double longitude = marker.Longitude;
                        SetStartOrEndRoute(new PointLatLng(latitude, longitude));
                    }
                }
                void GMapControlOnMarkerEnter(GMapMarker item) {
                    if (item is AgencyMarker agencyMarker) {
                        agencyMarker.ShowToolTip();
                    }
                }
                void GMapControlSelectionAreaChange(RectLatLng selection, bool zoomtofit) {
                    var (b, a, d, c) = selection.Cast();
                    this.OverpassUrlTextBox.Text = string.Format(OverpassPattern, a,b,c,d);
                }
            }
        }
        
        //放置marker的图层
        private GMapOverlay markersOverlay = new GMapOverlay("markers");

        private GMapRoute Route(PointLatLng start, PointLatLng end) {
            //根据起止点start、end和当前的zoom查找路径
            if (MapRoutingProvider == null) {
                Console.WriteLine($@"没有加载Osm导航数据");
                return null;
            }
            if (!MapRoutingProvider.IsConnected(start) || !MapRoutingProvider.IsConnected(end)) {
                Console.WriteLine($@"起点或终点无法连接至路网，请检查是否超出当前Osm文件区域:{GMapControl.SelectedArea.Cast()}");
                return null;
            }
            MapRoute route = MapRoutingProvider.GetRoute(start, end, false, false, (int) GMapControl.Zoom);
            if (route == null) {
                Console.WriteLine($@"没能找到起点到终点之间的路径，请检查是否超出当前Osm文件区域:{GMapControl.SelectedArea.Cast()}");
                return null;
            }
            return DrawRoute(route);
        }

        private GMapRoute DrawRoute(MapRoute route) {
            Route originRoute = (Route)route.Tag;
            //将路转换成线
            GMapRoute mapRoute = new GMapRoute(route.Points, route.Name);
            CurrentRoute = mapRoute;
            mapRoute.Stroke.Width = 6;
            mapRoute.Stroke.Color = Color.FromArgb(74, 149, 255);
            markersOverlay.Routes.Add(mapRoute);
            RouteDistanceTextBox.Text = $@"{originRoute.TotalDistance / 1000}";
            RouteIdealTimeTextBox.Text = $@"{TimeSpan.FromSeconds(originRoute.TotalTime)}";
            Console.WriteLine($@"导航成功，距离：{originRoute.TotalDistance / 1000} km，全速行程时间：{TimeSpan.FromSeconds(originRoute.TotalTime)}");
            return mapRoute;
        }

        private bool StartIndex = true;
        
        private GMapRoute CurrentRoute;
        
        private GMapMarker CurrentStart;

        private GMapMarker CurrentEnd;
        
        private void SetStartOrEndRoute(PointLatLng point) {
            GMapMarker marker = new GMarkerGoogle(point, GMarkerGoogleType.lightblue);
            markersOverlay.Markers.Add(marker);
            if (StartIndex) {
                if (CurrentStart != null) {
                    markersOverlay.Markers.Remove(CurrentStart);
                }
                if (CurrentEnd != null) {
                    markersOverlay.Markers.Remove(CurrentEnd);
                }                    
                if (CurrentRoute != null) {
                    markersOverlay.Routes.Remove(CurrentRoute);
                }
                CurrentStart = marker;
                StartPointTextBox.Text = $@"{point.Lat},{point.Lng}";
                Console.WriteLine($@"设置起点：{point.ToString()}");
                StartIndex = false;
            } else {
                CurrentEnd = marker;
                EndPointTextBox.Text = $@"{point.Lat},{point.Lng}";
                Console.WriteLine($@"设置终点：{point.ToString()}");
                StartIndex = true;
                CurrentRoute = Route(CurrentStart.Position, CurrentEnd.Position);
            }
        }

        private void SelectStartOrEnd_MouseClick(object sender, MouseEventArgs e) {
            if (e.Button == MouseButtons.Left) {
                PointLatLng point = GMapControl.FromLocalToLatLng(e.X, e.Y);
                SetStartOrEndRoute(point);
            }
        }

        private void OpenOsmFile_Click(object sender, EventArgs e) {
            OpenFileDialog.InitialDirectory = Environment.SpecialFolder.Desktop.ToString();
            OpenFileDialog.RestoreDirectory = true;
            string fileName = FileUtil.OpenFile(OpenFileDialog);
            if (fileName != null) {
                FileTextBox.Text = fileName;
            }
        }

        private void LoadOsmFileButton_Click(object sender, EventArgs e) {
            if (!File.Exists(FileTextBox.Text)) {
                Console.WriteLine($@"加载路网失败: {FileTextBox.Text}, 路网文件路径不正确");
                return;
            }
            if (LoadOsmWorker.IsBusy) {
                Console.WriteLine($@"正在加载路网中，请稍后");
                return;
            }
            Console.WriteLine($@"开始加载Osm文件: {DateTime.Now}，可能会消耗一段时间");
            LoadFileButton.Enabled = false;
            LoadOsmWorker.RunWorkerAsync(Stopwatch.GetTimestamp());
        }

        private void LoadOsmWorker_DoWork(object sender, DoWorkEventArgs e) {
            OsmRouterData sucess = OsmLoadUtil.LoadRouterByOsmFile(FileTextBox.Text, NavigateProfileProvider.Profiles);
            if (!sucess.Success) {
                Console.WriteLine($@"加载路网文件{{FileTextBox.Text}}失败: {sucess.Message}");
                return;
            }
            MapRoutingProvider = new OpenStreetMapRoutingProvider(sucess.Router, 
                NavigateProfileProvider.GetProfileByNavigateType(ConfigManager.UserSimulationConfig.导航类型));
            long timeSpan = Stopwatch.GetTimestamp() - Convert.ToInt64(e.Argument);
            Console.WriteLine(@$"加载路网成功: {FileTextBox.Text}，选择区域：{sucess.Bounds}，耗时：{TimeSpan.FromTicks(timeSpan)}");
            GMapControl.Invoke(new Action(()=> {
                GMapControl.SelectedArea = sucess.Bounds.Cast();
                GMapControl.SetZoomToFitRect(GMapControl.SelectedArea);
            }));
        }
        
        private void LoadOsmWorker_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e) {
            GMapControl.Invoke(new Action(()=> {
                LoadFileButton.Enabled = true;
            }));
        }

        private void ShowAllAgenciesButton_Click(object sender, EventArgs e) {
            if (!GMapControl.ZoomAndCenterMarkers(markersOverlay.Id)) {
                Console.WriteLine($@"没有站台");
            }
        }

        private void TextBox_KeyDown(object sender, KeyEventArgs e) {
            TextBox textBox = sender as TextBox;
            if (e.Control && textBox != null) {
                if (e.KeyCode == Keys.A) {
                    textBox.SelectAll();
                }else if (e.KeyCode == Keys.C) {
                    Clipboard.SetText(textBox.Text);
                }
            }
        }

        private void ShowOsmLoadAreaButton_Click(object sender, EventArgs e) {
            if (GMapControl.SelectedArea.IsEmpty) {
                Console.WriteLine($@"选择区域为空");
                return;
            }
            GMapControl.SetZoomToFitRect(GMapControl.SelectedArea);
        }

        private void DownloadOsmButton_Click(object sender, EventArgs e) {
            if (DownloadOsmWorker.IsBusy) {
                Console.WriteLine($@"正在下载中，请稍后");
                return;
            }
            Console.WriteLine($@"开始从overpass下载routerdb文件:{DownloadOsmTextBox.Text}");
            DownloadOsmWorker.RunWorkerAsync(Stopwatch.GetTimestamp());
            DownloadOsmProgressBar.Value = 0;
            DownloadOsmButton.Enabled = false;
        }

        private void DownloadOsmWorker_DoWork(object sender, DoWorkEventArgs e) {
            if (GMapControl.SelectedArea.IsEmpty) {
                Console.WriteLine($@"选择区域为空");
                return;
            }
            if (!DownloadOsmTextBox.Text.EndsWith(@".routerdb")) {
                Console.WriteLine($@"下载失败，文件应该是[.routerdb]后缀:{DownloadOsmTextBox.Text}");
                return;
            }
            string fileName = DownloadOsmTextBox.Text;
            if (File.Exists(fileName)) {
                Console.WriteLine($@"下载失败，文件已存在:{DownloadOsmTextBox.Text}");
                return;
            }
            Bound bound = GMapControl.SelectedArea.Cast();
            DownloadOsmProgressBar.Increment(10);
            OsmRouterData routerData = OsmLoadUtil.LoadRouterAndSaveFromOverpass(fileName, bound, NavigateProfileProvider.Profiles);
            if (!routerData.Success) {
                Console.WriteLine($@"下载失败:{routerData.Message}");
                return;
            }
            DownloadOsmProgressBar.Increment(90);
            long timeSpan = Stopwatch.GetTimestamp() - Convert.ToInt64(e.Argument);
            Console.WriteLine($@"下载成功: {fileName}，选择区域：{bound}，耗时：{TimeSpan.FromTicks(timeSpan)}");
            DownloadOsmProgressBar.Increment(100);
        }

        private void OsmSavePathButton_Click(object sender, EventArgs e) {
            string saveFilePath = FileUtil.SaveFile(SaveFileDialog, @"Osm文件", @"Osm文件|*.osm", "shk.osm");
            if (saveFilePath != null) {
                DownloadOsmTextBox.Text = saveFilePath;
            }
        }

        private void OpenOsmUrlButton_Click(object sender, EventArgs e) {
            if (!string.IsNullOrEmpty(OverpassUrlTextBox.Text)) {
                try {
                    var psi = new ProcessStartInfo {
                        FileName = OverpassUrlTextBox.Text,
                        UseShellExecute = true
                    };
                    Process.Start(psi);
                } catch (Exception exception) {
                    Console.WriteLine(exception);
                    MessageBox.Show(@"啊，手动复制打开吧", @"打开失败了");
                }
            }
        }

        private void DownloadOsmWorker_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e) {
            DownloadOsmButton.Invoke(new Action(() => {
                DownloadOsmButton.Enabled = true;
            }));
        }
    }
}
